#define _CRT_SECURE_NO_WARNINGS

#include "printers.h"
#include "file_cmd.h"
#include "int_util.h"

/****************************************************************************\
* Private Variables/Functions
\****************************************************************************/

#define S_MAX_COL_TYPE_NAME_LEN 31  // CodedToken:CustomAttributeType(30 chars)

static void s_format_col_type_name(pe_file *p_pe,
	u1 col_type, str_w str_buf, size_t str_len)
{
	wmemset(str_buf, L'\0', str_len);

	if(col_type <= MDCOLT_RID__MAX)
	{
		wcscat(str_buf, L"RID:");
		wcscat(str_buf, p_pe->metadata.tbl_defs[col_type].name);
	}
	else if(col_type >= MDCOLT_CT__MIN && col_type <= MDCOLT_CT__MAX)
	{
		wcscat(str_buf, L"CodedToken:");
		wcscat(str_buf, p_pe->metadata.coded_tokens[col_type - MDCOLT_CT__MIN].name);
	}
	else
	{
		switch(col_type)
		{
		case MDCOLT_I2:
			wcscpy(str_buf, L"SHORT");
			break;
		case MDCOLT_U2:
			wcscpy(str_buf, L"USHORT");
			break;
		case MDCOLT_I4:
			wcscpy(str_buf, L"LONG");
			break;
		case MDCOLT_U4:
			wcscpy(str_buf, L"ULONG");
			break;
		case MDCOLT_U1:
			wcscpy(str_buf, L"BYTE");
			break;
		case MDCOLT_S_STRINGS:
			wcscpy(str_buf, L"Stream:#Strings");
			break;
		case MDCOLT_S_GUID:
			wcscpy(str_buf, L"Stream:#GUID");
			break;
		case MDCOLT_S_BLOB:
			wcscpy(str_buf, L"Stream:#Blob");
			break;
		default:
			swprintf(str_buf, str_len, L"UNKNOWN:%Xh(%d)", col_type, col_type);
			break;
		}
	}
}

int s_format_type_flags(cor_type_flags val, str_w str_buf, size_t str_len)
{
	int nw = 0;

	switch(val & tdVisibilityMask)
	{
	case tdNotPublic:         nw += swprintf(str_buf + nw, str_len - nw, L"tdNotPublic ");         break;
	case tdPublic:            nw += swprintf(str_buf + nw, str_len - nw, L"tdPublic ");            break;
	case tdNestedPublic:      nw += swprintf(str_buf + nw, str_len - nw, L"tdNestedPublic ");      break;
	case tdNestedPrivate:     nw += swprintf(str_buf + nw, str_len - nw, L"tdNestedPrivate ");     break;
	case tdNestedFamily:      nw += swprintf(str_buf + nw, str_len - nw, L"tdNestedFamily ");      break;
	case tdNestedAssembly:    nw += swprintf(str_buf + nw, str_len - nw, L"tdNestedAssembly ");    break;
	case tdNestedFamANDAssem: nw += swprintf(str_buf + nw, str_len - nw, L"tdNestedFamANDAssem "); break;
	case tdNestedFamORAssem:  nw += swprintf(str_buf + nw, str_len - nw, L"tdNestedFamORAssem ");  break;
	}

	switch(val & tdLayoutMask)
	{
	case tdAutoLayout:       nw += swprintf(str_buf + nw, str_len - nw, L"tdAutoLayout ");       break;
	case tdSequentialLayout: nw += swprintf(str_buf + nw, str_len - nw, L"tdSequentialLayout "); break;
	case tdExplicitLayout:   nw += swprintf(str_buf + nw, str_len - nw, L"tdExplicitLayout ");   break;
	}

	switch(val & tdClassSemanticsMask)
	{
	case tdClass:     nw += swprintf(str_buf + nw, str_len - nw, L"tdClass ");     break;
	case tdInterface: nw += swprintf(str_buf + nw, str_len - nw, L"tdInterface "); break;
	}

	if(val & tdAbstract)     nw += swprintf(str_buf + nw, str_len - nw, L"tdAbstract ");
	if(val & tdSealed)       nw += swprintf(str_buf + nw, str_len - nw, L"tdSealed ");
	if(val & tdSpecialName)  nw += swprintf(str_buf + nw, str_len - nw, L"tdSpecialName ");

	if(val & tdImport)       nw += swprintf(str_buf + nw, str_len - nw, L"tdImport ");
	if(val & tdSerializable) nw += swprintf(str_buf + nw, str_len - nw, L"tdSerializable ");

	switch(val & tdStringFormatMask)
	{
	case tdAnsiClass:         nw += swprintf(str_buf + nw, str_len - nw, L"tdAnsiClass ");         break;
	case tdUnicodeClass:      nw += swprintf(str_buf + nw, str_len - nw, L"tdUnicodeClass ");      break;
	case tdAutoClass:         nw += swprintf(str_buf + nw, str_len - nw, L"tdAutoClass ");         break;
	case tdCustomFormatClass:
		nw += swprintf(str_buf + nw, str_len - nw, L"tdCustomFormatClass:%08Xh ", val & tdCustomFormatMask);
		break;
	}

	if(val & tdBeforeFieldInit) nw += swprintf(str_buf + nw, str_len - nw, L"tdBeforeFieldInit ");
	if(val & tdForwarder)       nw += swprintf(str_buf + nw, str_len - nw, L"tdForwarder ");
	if(val & tdRTSpecialName) nw += swprintf(str_buf + nw, str_len - nw, L"tdRTSpecialName ");
	if(val & tdHasSecurity)   nw += swprintf(str_buf + nw, str_len - nw, L"tdHasSecurity ");

	return nw;
}

int s_format_field_flags(cor_field_flags val, str_w str_buf, size_t str_len)
{
	int nw = 0;

	switch(val & fdFieldAccessMask)
	{
	case fdPrivateScope: nw += swprintf(str_buf + nw, str_len - nw, L"fdPrivateScope "); break;
	case fdPrivate:      nw += swprintf(str_buf + nw, str_len - nw, L"fdPrivate ");      break;
	case fdFamANDAssem:  nw += swprintf(str_buf + nw, str_len - nw, L"fdFamANDAssem ");  break;
	case fdAssembly:     nw += swprintf(str_buf + nw, str_len - nw, L"fdAssembly ");     break;
	case fdFamily:       nw += swprintf(str_buf + nw, str_len - nw, L"fdFamily ");       break;
	case fdFamORAssem:   nw += swprintf(str_buf + nw, str_len - nw, L"fdFamORAssem ");   break;
	case fdPublic:       nw += swprintf(str_buf + nw, str_len - nw, L"fdPublic ");       break;
	}

	if(val & fdStatic)          nw += swprintf(str_buf + nw, str_len - nw, L"fdStatic ");
	if(val & fdInitOnly)        nw += swprintf(str_buf + nw, str_len - nw, L"fdInitOnly ");
	if(val & fdLiteral)         nw += swprintf(str_buf + nw, str_len - nw, L"fdLiteral ");
	if(val & fdNotSerialized)   nw += swprintf(str_buf + nw, str_len - nw, L"fdNotSerialized ");
	if(val & fdSpecialName)     nw += swprintf(str_buf + nw, str_len - nw, L"fdSpecialName ");
	if(val & fdPinvokeImpl)     nw += swprintf(str_buf + nw, str_len - nw, L"fdPinvokeImpl ");
	if(val & fdRTSpecialName)   nw += swprintf(str_buf + nw, str_len - nw, L"fdRTSpecialName ");
	if(val & fdHasFieldMarshal) nw += swprintf(str_buf + nw, str_len - nw, L"fdHasFieldMarshal ");
	if(val & fdHasDefault)      nw += swprintf(str_buf + nw, str_len - nw, L"fdHasDefault ");
	if(val & fdHasFieldRVA)     nw += swprintf(str_buf + nw, str_len - nw, L"fdHasFieldRVA ");

	return nw;
}

int s_format_method_impl_flags(cor_method_impl_flags val, str_w str_buf, size_t str_len)
{
	int nw = 0;

	switch(val & miCodeTypeMask)
	{
	case miIL:      nw += swprintf(str_buf + nw, str_len - nw, L"miIL ");      break;
	case miNative:  nw += swprintf(str_buf + nw, str_len - nw, L"miNative ");  break;
	case miOPTIL:   nw += swprintf(str_buf + nw, str_len - nw, L"miOPTIL ");   break;
	case miRuntime: nw += swprintf(str_buf + nw, str_len - nw, L"miRuntime "); break;
	}

	switch(val & miManagedMask)
	{
	case miUnmanaged: nw += swprintf(str_buf + nw, str_len - nw, L"miUnmanaged "); break;
	case miManaged:   nw += swprintf(str_buf + nw, str_len - nw, L"miManaged ");   break;
	}

	if(val & miForwardRef)     nw += swprintf(str_buf + nw, str_len - nw, L"miForwardRef ");
	if(val & miPreserveSig)    nw += swprintf(str_buf + nw, str_len - nw, L"miPreserveSig ");
	if(val & miInternalCall)   nw += swprintf(str_buf + nw, str_len - nw, L"miInternalCall ");
	if(val & miSynchronized)   nw += swprintf(str_buf + nw, str_len - nw, L"miSynchronized ");
	if(val & miNoInlining)     nw += swprintf(str_buf + nw, str_len - nw, L"miNoInlining ");
	if(val & miNoOptimization) nw += swprintf(str_buf + nw, str_len - nw, L"miNoOptimization ");

	return nw;
}

int s_format_method_flags(cor_method_flags val, str_w str_buf, size_t str_len)
{
	int nw = 0;

	switch(val & mdMemberAccessMask)
	{
	case mdPrivateScope: nw += swprintf(str_buf + nw, str_len - nw, L"mdPrivateScope "); break;
	case mdPrivate:      nw += swprintf(str_buf + nw, str_len - nw, L"mdPrivate ");      break;
	case mdFamANDAssem:  nw += swprintf(str_buf + nw, str_len - nw, L"mdFamANDAssem ");  break;
	case mdAssem:        nw += swprintf(str_buf + nw, str_len - nw, L"mdAssem ");        break;
	case mdFamily:       nw += swprintf(str_buf + nw, str_len - nw, L"mdFamily ");       break;
	case mdFamORAssem:   nw += swprintf(str_buf + nw, str_len - nw, L"mdFamORAssem ");   break;
	case mdPublic:       nw += swprintf(str_buf + nw, str_len - nw, L"mdPublic ");       break;
	}

	if(val & mdStatic)    nw += swprintf(str_buf + nw, str_len - nw, L"mdStatic ");
	if(val & mdFinal)     nw += swprintf(str_buf + nw, str_len - nw, L"mdFinal ");
	if(val & mdVirtual)   nw += swprintf(str_buf + nw, str_len - nw, L"mdVirtual ");
	if(val & mdHideBySig) nw += swprintf(str_buf + nw, str_len - nw, L"mdHideBySig ");

	switch(val & mdVtableLayoutMask)
	{
	case mdReuseSlot: nw += swprintf(str_buf + nw, str_len - nw, L"mdReuseSlot "); break;
	case mdNewSlot:   nw += swprintf(str_buf + nw, str_len - nw, L"mdNewSlot ");   break;
	}

	if(val & mdCheckAccessOnOverride) nw += swprintf(str_buf + nw, str_len - nw, L"mdCheckAccessOnOverride ");
	if(val & mdAbstract)              nw += swprintf(str_buf + nw, str_len - nw, L"mdAbstract ");
	if(val & mdSpecialName)           nw += swprintf(str_buf + nw, str_len - nw, L"mdSpecialName ");
	if(val & mdPinvokeImpl)           nw += swprintf(str_buf + nw, str_len - nw, L"mdPinvokeImpl ");
	if(val & mdUnmanagedExport)       nw += swprintf(str_buf + nw, str_len - nw, L"mdUnmanagedExport ");
	if(val & mdRTSpecialName)         nw += swprintf(str_buf + nw, str_len - nw, L"mdRTSpecialName ");
	if(val & mdHasSecurity)           nw += swprintf(str_buf + nw, str_len - nw, L"mdHasSecurity ");
	if(val & mdRequireSecObject)      nw += swprintf(str_buf + nw, str_len - nw, L"mdRequireSecObject ");

	return nw;
}

int s_format_param_flags(cor_param_flags val, str_w str_buf, size_t str_len)
{
	int nw = 0;

	if(val & pdIn)              nw += swprintf(str_buf + nw, str_len - nw, L"pdIn ");
	if(val & pdOut)             nw += swprintf(str_buf + nw, str_len - nw, L"pdOut ");
	if(val & pdOptional)        nw += swprintf(str_buf + nw, str_len - nw, L"pdOptional ");
	if(val & pdHasDefault)      nw += swprintf(str_buf + nw, str_len - nw, L"pdHasDefault ");
	if(val & pdHasFieldMarshal) nw += swprintf(str_buf + nw, str_len - nw, L"pdHasFieldMarshal ");

	return nw;
}

int s_format_event_flags(cor_event_flags val, str_w str_buf, size_t str_len)
{
	int nw = 0;

	if(val & evSpecialName)   nw += swprintf(str_buf + nw, str_len - nw, L"evSpecialName ");
	if(val & evRTSpecialName) nw += swprintf(str_buf + nw, str_len - nw, L"evRTSpecialName ");

	return nw;
}

int s_format_property_flags(cor_property_flags val, str_w str_buf, size_t str_len)
{
	int nw = 0;

	if(val & prSpecialName)   nw += swprintf(str_buf + nw, str_len - nw, L"prSpecialName ");
	if(val & prRTSpecialName) nw += swprintf(str_buf + nw, str_len - nw, L"prRTSpecialName ");
	if(val & prHasDefault)    nw += swprintf(str_buf + nw, str_len - nw, L"prHasDefault ");

	return nw;
}

int s_format_method_semantics_flags(cor_method_semantics_flags val, str_w str_buf, size_t str_len)
{
	int nw = 0;

	switch(val)
	{
	case msSetter:   nw += swprintf(str_buf + nw, str_len - nw, L"msSetter ");   break;
	case msGetter:   nw += swprintf(str_buf + nw, str_len - nw, L"msGetter ");   break;
	case msOther:    nw += swprintf(str_buf + nw, str_len - nw, L"msOther ");    break;
	case msAddOn:    nw += swprintf(str_buf + nw, str_len - nw, L"msAddOn ");    break;
	case msRemoveOn: nw += swprintf(str_buf + nw, str_len - nw, L"msRemoveOn "); break;
	case msFire:     nw += swprintf(str_buf + nw, str_len - nw, L"msFire ");     break;
	}

	return nw;
}

int s_format_pinvoke_map_flags(cor_pinvoke_map_flags val, str_w str_buf, size_t str_len)
{
	int nw = 0;

	if(val & pmNoMangle) nw += swprintf(str_buf + nw, str_len - nw, L"pmNoMangle ");

	switch(val & mdVtableLayoutMask)
	{
	case pmCharSetNotSpec: nw += swprintf(str_buf + nw, str_len - nw, L"pmCharSetNotSpec "); break;
	case pmCharSetAnsi:    nw += swprintf(str_buf + nw, str_len - nw, L"pmCharSetAnsi ");    break;
	case pmCharSetUnicode: nw += swprintf(str_buf + nw, str_len - nw, L"pmCharSetUnicode "); break;
	case pmCharSetAuto:    nw += swprintf(str_buf + nw, str_len - nw, L"pmCharSetAuto ");    break;
	}

	switch(val & pmBestFitMask)
	{
	case pmBestFitUseAssem: nw += swprintf(str_buf + nw, str_len - nw, L"pmBestFitUseAssem "); break;
	case pmBestFitEnabled:  nw += swprintf(str_buf + nw, str_len - nw, L"pmBestFitEnabled ");  break;
	case pmBestFitDisabled: nw += swprintf(str_buf + nw, str_len - nw, L"pmBestFitDisabled "); break;
	}

	switch(val & pmThrowOnUnmappableCharMask)
	{
	case pmThrowOnUnmappableCharUseAssem: nw += swprintf(str_buf + nw, str_len - nw, L"pmThrowOnUnmappableCharUseAssem "); break;
	case pmThrowOnUnmappableCharEnabled:  nw += swprintf(str_buf + nw, str_len - nw, L"pmThrowOnUnmappableCharEnabled ");  break;
	case pmThrowOnUnmappableCharDisabled: nw += swprintf(str_buf + nw, str_len - nw, L"pmThrowOnUnmappableCharDisabled "); break;
	}

	if(val & pmSupportsLastError) nw += swprintf(str_buf + nw, str_len - nw, L"pmSupportsLastError ");

	switch(val & pmCallConvMask)
	{
	case pmCallConvWinapi:   nw += swprintf(str_buf + nw, str_len - nw, L"pmCallConvWinapi ");   break;
	case pmCallConvCdecl:    nw += swprintf(str_buf + nw, str_len - nw, L"pmCallConvCdecl ");    break;
	case pmCallConvStdcall:  nw += swprintf(str_buf + nw, str_len - nw, L"pmCallConvStdcall ");  break;
	case pmCallConvThiscall: nw += swprintf(str_buf + nw, str_len - nw, L"pmCallConvThiscall "); break;
	case pmCallConvFastcall: nw += swprintf(str_buf + nw, str_len - nw, L"pmCallConvFastcall "); break;
	}

	return nw;
}

int s_format_assembly_flags(cor_assembly_flags val, str_w str_buf, size_t str_len)
{
	int nw = 0;

	if(val & afPublicKey) nw += swprintf(str_buf + nw, str_len - nw, L"afPublicKey ");

	switch(val & afPA_FullMask)
	{
	case afPA_None:   nw += swprintf(str_buf + nw, str_len - nw, L"afPA_None ");            break;
	case afPA_MSIL:    nw += swprintf(str_buf + nw, str_len - nw, L"afPA_MSIL ");           break;
	case afPA_x86:  nw += swprintf(str_buf + nw, str_len - nw, L"afPA_x86 ");               break;
	case afPA_IA64: nw += swprintf(str_buf + nw, str_len - nw, L"afPA_IA64 ");              break;
	case afPA_AMD64: nw += swprintf(str_buf + nw, str_len - nw, L"afPA_AMD64 ");            break;
	case afPA_NoPlatform:  nw += swprintf(str_buf + nw, str_len - nw, L"afPA_NoPlatform "); break;
	case afPA_Specified: nw += swprintf(str_buf + nw, str_len - nw, L"afPA_Specified ");    break;
	}

	if(val & afEnableJITcompileTracking)   nw += swprintf(str_buf + nw, str_len - nw, L"afEnableJITcompileTracking ");
	if(val & afDisableJITcompileOptimizer) nw += swprintf(str_buf + nw, str_len - nw, L"afDisableJITcompileOptimizer ");
	if(val & afRetargetable)               nw += swprintf(str_buf + nw, str_len - nw, L"afRetargetable ");

	return nw;
}

int s_format_file_flags(cor_file_flags val, str_w str_buf, size_t str_len)
{
	int nw = 0;

	switch(val)
	{
	case ffContainsMetaData:   nw += swprintf(str_buf + nw, str_len - nw, L"ffContainsMetaData ");   break;
	case ffContainsNoMetaData: nw += swprintf(str_buf + nw, str_len - nw, L"ffContainsNoMetaData "); break;
	}

	return nw;
}

int s_format_manifest_resource_flags(cor_manifest_resource_flags val, str_w str_buf, size_t str_len)
{
	int nw = 0;

	switch(val & mrVisibilityMask)
	{
	case mrPublic:  nw += swprintf(str_buf + nw, str_len - nw, L"mrPublic ");  break;
	case mrPrivate: nw += swprintf(str_buf + nw, str_len - nw, L"mrPrivate "); break;
	}

	return nw;
}

int s_format_generic_param_flags(cor_generic_param_flags val, str_w str_buf, size_t str_len)
{
	int nw = 0;

	switch(val & gpVarianceMask)
	{
	case gpNonVariant:    nw += swprintf(str_buf + nw, str_len - nw, L"gpNonVariant ");    break;
	case gpCovariant:     nw += swprintf(str_buf + nw, str_len - nw, L"gpCovariant ");     break;
	case gpContravariant: nw += swprintf(str_buf + nw, str_len - nw, L"gpContravariant "); break;
	}

	switch(val & gpSpecialConstraintMask)
	{
	case gpNoSpecialConstraint:            nw += swprintf(str_buf + nw, str_len - nw, L"gpNoSpecialConstraint ");            break;
	case gpReferenceTypeConstraint:        nw += swprintf(str_buf + nw, str_len - nw, L"gpReferenceTypeConstraint ");        break;
	case gpNotNullableValueTypeConstraint: nw += swprintf(str_buf + nw, str_len - nw, L"gpNotNullableValueTypeConstraint "); break;
	case gpDefaultConstructorConstraint:   nw += swprintf(str_buf + nw, str_len - nw, L"gpDefaultConstructorConstraint ");   break;
	}

	return nw;
}

int s_format_hash_alg_id(cor_hash_alg_id val, str_w str_buf, size_t str_len)
{
	int nw = 0;

	switch(val)
	{
	case haNone: nw += swprintf(str_buf + nw, str_len - nw, L"haNone "); break;
	case haMD5:  nw += swprintf(str_buf + nw, str_len - nw, L"haMD5 ");  break;
	case haSha1: nw += swprintf(str_buf + nw, str_len - nw, L"haSha1 "); break;
	}

	return nw;
}

int s_format_security_action(cor_security_action val, str_w str_buf, size_t str_len)
{
	int nw = 0;

	switch(val)
	{
	case saRequest:                 nw += swprintf(str_buf + nw, str_len - nw, L"saRequest ");                 break;
	case saDemand:                  nw += swprintf(str_buf + nw, str_len - nw, L"saDemand ");                  break;
	case saAssert:                  nw += swprintf(str_buf + nw, str_len - nw, L"saAssert ");                  break;
	case saDeny:                    nw += swprintf(str_buf + nw, str_len - nw, L"saDeny ");                    break;
	case saPermitOnly:              nw += swprintf(str_buf + nw, str_len - nw, L"saPermitOnly ");              break;
	case saLinkDemand:              nw += swprintf(str_buf + nw, str_len - nw, L"saLinkDemand ");              break;
	case saInheritanceDemand:       nw += swprintf(str_buf + nw, str_len - nw, L"saInheritanceDemand ");       break;
	case saRequestMinimum:          nw += swprintf(str_buf + nw, str_len - nw, L"saRequestMinimum ");          break;
	case saRequestOptional:         nw += swprintf(str_buf + nw, str_len - nw, L"saRequestOptional ");         break;
	case saRequestRefuse:           nw += swprintf(str_buf + nw, str_len - nw, L"saRequestRefuse ");           break;
	case saPreJitGrant:             nw += swprintf(str_buf + nw, str_len - nw, L"saPreJitGrant ");             break;
	case saPreJitDeny:              nw += swprintf(str_buf + nw, str_len - nw, L"saPreJitDeny ");              break;
	case saNonCasDemand:            nw += swprintf(str_buf + nw, str_len - nw, L"saNonCasDemand ");            break;
	case saNonCasLinkDemand:        nw += swprintf(str_buf + nw, str_len - nw, L"saNonCasLinkDemand ");        break;
	case saNonCasInheritanceDemand: nw += swprintf(str_buf + nw, str_len - nw, L"saNonCasInheritanceDemand "); break;
	}

	return nw;
}

int s_format_element_type(cor_element_type val, str_w str_buf, size_t str_len)
{
	int nw = 0;

	switch(val)
	{
	case ELEMENT_TYPE_END:         nw += swprintf(str_buf + nw, str_len - nw, L"(ELEMENT_TYPE_END)");         break;
	case ELEMENT_TYPE_VOID:        nw += swprintf(str_buf + nw, str_len - nw, L"(ELEMENT_TYPE_VOID)");        break;
	case ELEMENT_TYPE_BOOLEAN:     nw += swprintf(str_buf + nw, str_len - nw, L"(ELEMENT_TYPE_BOOLEAN)");     break;
	case ELEMENT_TYPE_CHAR:        nw += swprintf(str_buf + nw, str_len - nw, L"(ELEMENT_TYPE_CHAR)");        break;
	case ELEMENT_TYPE_I1:          nw += swprintf(str_buf + nw, str_len - nw, L"(ELEMENT_TYPE_I1)");          break;
	case ELEMENT_TYPE_U1:          nw += swprintf(str_buf + nw, str_len - nw, L"(ELEMENT_TYPE_U1)");          break;
	case ELEMENT_TYPE_I2:          nw += swprintf(str_buf + nw, str_len - nw, L"(ELEMENT_TYPE_I2)");          break;
	case ELEMENT_TYPE_U2:          nw += swprintf(str_buf + nw, str_len - nw, L"(ELEMENT_TYPE_U2)");          break;
	case ELEMENT_TYPE_I4:          nw += swprintf(str_buf + nw, str_len - nw, L"(ELEMENT_TYPE_I4)");          break;
	case ELEMENT_TYPE_U4:          nw += swprintf(str_buf + nw, str_len - nw, L"(ELEMENT_TYPE_U4)");          break;
	case ELEMENT_TYPE_I8:          nw += swprintf(str_buf + nw, str_len - nw, L"(ELEMENT_TYPE_I8)");          break;
	case ELEMENT_TYPE_U8:          nw += swprintf(str_buf + nw, str_len - nw, L"(ELEMENT_TYPE_U8)");          break;
	case ELEMENT_TYPE_R4:          nw += swprintf(str_buf + nw, str_len - nw, L"(ELEMENT_TYPE_R4)");          break;
	case ELEMENT_TYPE_R8:          nw += swprintf(str_buf + nw, str_len - nw, L"(ELEMENT_TYPE_R8)");          break;
	case ELEMENT_TYPE_STRING:      nw += swprintf(str_buf + nw, str_len - nw, L"(ELEMENT_TYPE_STRING)");      break;
	case ELEMENT_TYPE_PTR:         nw += swprintf(str_buf + nw, str_len - nw, L"(ELEMENT_TYPE_PTR)");         break;
	case ELEMENT_TYPE_BYREF:       nw += swprintf(str_buf + nw, str_len - nw, L"(ELEMENT_TYPE_BYREF)");       break;
	case ELEMENT_TYPE_VALUETYPE:   nw += swprintf(str_buf + nw, str_len - nw, L"(ELEMENT_TYPE_VALUETYPE)");   break;
	case ELEMENT_TYPE_CLASS:       nw += swprintf(str_buf + nw, str_len - nw, L"(ELEMENT_TYPE_CLASS)");       break;
	case ELEMENT_TYPE_VAR:         nw += swprintf(str_buf + nw, str_len - nw, L"(ELEMENT_TYPE_VAR)");         break;
	case ELEMENT_TYPE_ARRAY:       nw += swprintf(str_buf + nw, str_len - nw, L"(ELEMENT_TYPE_ARRAY)");       break;
	case ELEMENT_TYPE_GENERICINST: nw += swprintf(str_buf + nw, str_len - nw, L"(ELEMENT_TYPE_GENERICINST)"); break;
	case ELEMENT_TYPE_TYPEDBYREF:  nw += swprintf(str_buf + nw, str_len - nw, L"(ELEMENT_TYPE_TYPEDBYREF)");  break;
	case ELEMENT_TYPE_I:           nw += swprintf(str_buf + nw, str_len - nw, L"(ELEMENT_TYPE_I)");           break;
	case ELEMENT_TYPE_U:           nw += swprintf(str_buf + nw, str_len - nw, L"(ELEMENT_TYPE_U)");           break;
	case ELEMENT_TYPE_FNPTR:       nw += swprintf(str_buf + nw, str_len - nw, L"(ELEMENT_TYPE_FNPTR)");       break;
	case ELEMENT_TYPE_OBJECT:      nw += swprintf(str_buf + nw, str_len - nw, L"(ELEMENT_TYPE_OBJECT)");      break;
	case ELEMENT_TYPE_SZARRAY:     nw += swprintf(str_buf + nw, str_len - nw, L"(ELEMENT_TYPE_SZARRAY)");     break;
	case ELEMENT_TYPE_MVAR:        nw += swprintf(str_buf + nw, str_len - nw, L"(ELEMENT_TYPE_MVAR)");        break;
	case ELEMENT_TYPE_CMOD_REQD:   nw += swprintf(str_buf + nw, str_len - nw, L"(ELEMENT_TYPE_CMOD_REQD)");   break;
	case ELEMENT_TYPE_CMOD_OPT:    nw += swprintf(str_buf + nw, str_len - nw, L"(ELEMENT_TYPE_CMOD_OPT)");    break;
	case ELEMENT_TYPE_INTERNAL:    nw += swprintf(str_buf + nw, str_len - nw, L"(ELEMENT_TYPE_INTERNAL)");    break;
	case ELEMENT_TYPE_SENTINEL:    nw += swprintf(str_buf + nw, str_len - nw, L"(ELEMENT_TYPE_SENTINEL)");    break;
	case ELEMENT_TYPE_PINNED:      nw += swprintf(str_buf + nw, str_len - nw, L"(ELEMENT_TYPE_PINNED)");      break;
	case ELEMENT_TYPE_R4_HFA:      nw += swprintf(str_buf + nw, str_len - nw, L"(ELEMENT_TYPE_R4_HFA)");      break;
	case ELEMENT_TYPE_R8_HFA:      nw += swprintf(str_buf + nw, str_len - nw, L"(ELEMENT_TYPE_R8_HFA)");      break;
	default: swprintf(str_buf + nw, str_len - nw, L"(<UNKNOWN>)"); break;
	}

	return nw;
}

#define S_MAX_COL_VAL_LEN 256

static void s_format_col_val(pe_file *p_pe,
	mdt_tbl_def *p_tbl, mdt_col_def *p_col,
	u4 val, str_w str_buf, size_t str_len)
{
	size_t len;

	wmemset(str_buf, L'\0', str_len);
	switch(p_col->type)
	{
	case MDCOLT_RID_Module:
	case MDCOLT_RID_TypeRef:
	case MDCOLT_RID_TypeDef:
	case MDCOLT_RID_FieldPtr:
	case MDCOLT_RID_Field:
	case MDCOLT_RID_MethodPtr:
	case MDCOLT_RID_Method:
	case MDCOLT_RID_ParamPtr:
	case MDCOLT_RID_Param:
	case MDCOLT_RID_InterfaceImpl:
	case MDCOLT_RID_MemberRef:
	case MDCOLT_RID_Constant:
	case MDCOLT_RID_CustomAttribute:
	case MDCOLT_RID_FieldMarshal:
	case MDCOLT_RID_DeclSecurity:
	case MDCOLT_RID_ClassLayout:
	case MDCOLT_RID_FieldLayout:
	case MDCOLT_RID_StandAloneSig:
	case MDCOLT_RID_EventMap:
	case MDCOLT_RID_EventPtr:
	case MDCOLT_RID_Event:
	case MDCOLT_RID_PropertyMap:
	case MDCOLT_RID_PropertyPtr:
	case MDCOLT_RID_Property:
	case MDCOLT_RID_MethodSemantics:
	case MDCOLT_RID_MethodImpl:
	case MDCOLT_RID_ModuleRef:
	case MDCOLT_RID_TypeSpec:
	case MDCOLT_RID_ImplMap:
	case MDCOLT_RID_FieldRVA:
	case MDCOLT_RID_ENCLog:
	case MDCOLT_RID_ENCMap:
	case MDCOLT_RID_Assembly:
	case MDCOLT_RID_AssemblyProcessor:
	case MDCOLT_RID_AssemblyOS:
	case MDCOLT_RID_AssemblyRef:
	case MDCOLT_RID_AssemblyRefProcessor:
	case MDCOLT_RID_AssemblyRefOS:
	case MDCOLT_RID_File:
	case MDCOLT_RID_ExportedType:
	case MDCOLT_RID_ManifestResource:
	case MDCOLT_RID_NestedClass:
	case MDCOLT_RID_GenericParam:
	case MDCOLT_RID_MethodSpec:
	case MDCOLT_RID_GenericParamConstraint:
		swprintf(str_buf, str_len, L"RID:%s[%Xh (%d)]",
			p_pe->metadata.tbl_defs[p_col->type].name, val, val);
		break;

	case MDCOLT_CT_TypeDefOrRef:
	case MDCOLT_CT_HasConstant:
	case MDCOLT_CT_HasCustomAttribute:
	case MDCOLT_CT_HasFieldMarshal:
	case MDCOLT_CT_HasDeclSecurity:
	case MDCOLT_CT_MemberRefParent:
	case MDCOLT_CT_HasSemantics:
	case MDCOLT_CT_MethodDefOrRef:
	case MDCOLT_CT_MemberForwarded:
	case MDCOLT_CT_Implementation:
	case MDCOLT_CT_CustomAttributeType:
	case MDCOLT_CT_ResolutionScope:
	case MDCOLT_CT_TypeOrMethodDef:
		{
			u1 ctid = p_col->type - MDCOLT_CT__MIN;
			coded_token_def *p_ct = p_pe->metadata.coded_tokens + ctid;
			u1 tag = val & p_ct->tag_mask;
			u1 rid = val >> p_ct->tag_size;
			table_id rtid = p_pe->metadata.coded_token_ref_tbls[p_ct->tables + tag].table_id;
			mdt_tbl_def *p_ref_tbl = p_pe->metadata.tbl_defs + rtid;

			swprintf(str_buf, str_len, L"%s:%s[%Xh (%d)]",
				p_ct->name, p_ref_tbl->name, rid, rid);
		}
		break;

	case MDCOLT_S_STRINGS:
		swprintf(str_buf, str_len, L"#Strings[%Xh (%d)] ", val, val);
		if(val > 0)
		{
			char *p_str = (char *)(p_pe->p_base + p_pe->metadata.strings_offset + val);
			len = wcslen(str_buf);

			for(;len < str_len && p_str; ++len, ++p_str)
				str_buf[len] = *p_str;
		}
		break;

	case MDCOLT_S_GUID:
		swprintf(str_buf, str_len, L"#GUID[%Xh (%d)] ", val, val);
		if(val > 0)
		{
			guid *p_guid = (guid *)(p_pe->p_base + p_pe->metadata.guid_offset + (val - 1) * 16);
			len = wcslen(str_buf);
			swprintf(str_buf + len, str_len - len,
				L"{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
				p_guid->part1, p_guid->part2, p_guid->part3,
				p_guid->part4[0], p_guid->part4[1], p_guid->part4[2], p_guid->part4[3],
				p_guid->part4[4], p_guid->part4[5], p_guid->part4[6], p_guid->part4[7]);
		}
		break;

	case MDCOLT_S_BLOB:
		swprintf(str_buf, str_len, L"#Blob[%Xh (%d)] ", val, val);
		if(val > 0)
		{
			size_t item_sz_len;
			u4 item_val_len;
			byte *p_data = p_pe->p_base + p_pe->metadata.blob_offset + val;

			len = wcslen(str_buf);

			str_buf += len;
			str_len -= len;

			if(!decode_u4(p_data, &item_sz_len, &item_val_len))
				swprintf(str_buf, str_len, L"Cannot decode data.");
			else if(item_val_len > 0)
			{
				u4 n;
				p_data += item_sz_len;

				if(str_len < 2)
					return;

				swprintf(str_buf, str_len, L"%02X", p_data[0]);
				str_buf += 2;
				str_len -= 2;
				for(n = 1; n < item_val_len; ++n)
				{
					if(str_len < 3)
						return;

					swprintf(str_buf, str_len, L"-%02X", p_data[n]);
					str_buf += 3;
					str_len -= 3;
				}
			}
		}
		break;

	case MDCOLT_U1:
	case MDCOLT_U2:
	case MDCOLT_U4:
		len = wcslen(str_buf);
		switch(p_col->ex_type)
		{
		case MDCOLTEX_RVA:
			format_rva_str(p_pe, val, str_buf + len, str_len - len);
			break;
		case MDCOLTEX_TypeFlags:
			s_format_type_flags((cor_type_flags)val, str_buf + len, str_len - len);
			break;
		case MDCOLTEX_FieldFlags:
			s_format_field_flags((cor_field_flags)val, str_buf + len, str_len - len);
			break;
		case MDCOLTEX_MethodImplFlags:
			s_format_method_impl_flags((cor_method_impl_flags)val, str_buf + len, str_len - len);
			break;
		case MDCOLTEX_MethodFlags:
			s_format_method_flags((cor_method_flags)val, str_buf + len, str_len - len);
			break;
		case MDCOLTEX_ParamFlags:
			s_format_param_flags((cor_param_flags)val, str_buf + len, str_len - len);
			break;
		case MDCOLTEX_EventFlags:
			s_format_event_flags((cor_event_flags)val, str_buf + len, str_len - len);
			break;
		case MDCOLTEX_PropertyFlags:
			s_format_property_flags((cor_property_flags)val, str_buf + len, str_len - len);
			break;
		case MDCOLTEX_MethodSemanticsFlags:
			s_format_method_semantics_flags((cor_method_semantics_flags)val, str_buf + len, str_len - len);
			break;
		case MDCOLTEX_PInvokeMapFlags:
			s_format_pinvoke_map_flags((cor_pinvoke_map_flags)val, str_buf + len, str_len - len);
			break;
		case MDCOLTEX_AssemblyFlags:
			s_format_assembly_flags((cor_assembly_flags)val, str_buf + len, str_len - len);
			break;
		case MDCOLTEX_FileFlags:
			s_format_file_flags((cor_file_flags)val, str_buf + len, str_len - len);
			break;
		case MDCOLTEX_ManifestResourceFlags:
			s_format_manifest_resource_flags((cor_manifest_resource_flags)val, str_buf + len, str_len - len);
			break;
		case MDCOLTEX_GenericParamFlags:
			s_format_generic_param_flags((cor_generic_param_flags)val, str_buf + len, str_len - len);
			break;
		case MDCOLTEX_HashAlg:
			s_format_hash_alg_id((cor_hash_alg_id)val, str_buf + len, str_len - len);
			break;
		case MDCOLTEX_SecurityAction:
			s_format_security_action((cor_security_action)val, str_buf + len, str_len - len);
			break;
		case MDCOLTEX_ElementType:
			s_format_element_type((cor_element_type)val, str_buf + len, str_len - len);
			break;
		default:
			swprintf(str_buf + len, str_len - len, L"(%d)", val);
			break;
		}

	default:
		break;
	}
}

/****************************************************************************\
* Public Functions
\****************************************************************************/

void print_mdt_hdr(pe_file *p_pe)
{
	byte *pb = p_pe->p_base;
	metadata_table_header *ph = p_pe->metadata.p_table_hdr;

	if(!check_file_opened() || !check_pe_headers(p_pe) || !check_metadata(p_pe))
		return;

	wprintf(L"Metadata Table Header:\n");

	wprintf(L"%08Xh ", PTR_D(pb, &ph->Reserved));
	wprintf(L"%01Xh ", sizeof(ph->Reserved));
	wprintf(L"%11s ", L"Reserved");
	wprintf(L"%08Xh\n", ph->Reserved);

	wprintf(L"%08Xh ", PTR_D(pb, &ph->Major));
	wprintf(L"%01Xh ", sizeof(ph->Major));
	wprintf(L"%11s ", L"Major");
	wprintf(L"%02Xh               (%d)\n", ph->Major, ph->Major);

	wprintf(L"%08Xh ", PTR_D(pb, &ph->Minor));
	wprintf(L"%01Xh ", sizeof(ph->Minor));
	wprintf(L"%11s ", L"Minor");
	wprintf(L"%02Xh               (%d)\n", ph->Minor, ph->Minor);

	wprintf(L"%08Xh ", PTR_D(pb, &ph->Heaps));
	wprintf(L"%01Xh ", sizeof(ph->Heaps));
	wprintf(L"%11s ", L"Heaps");
	wprintf(L"%02Xh\n", ph->Heaps);

	wprintf(L"%08Xh ", PTR_D(pb, &ph->Rid));
	wprintf(L"%01Xh ", sizeof(ph->Rid));
	wprintf(L"%11s ", L"Rid");
	wprintf(L"%02Xh\n", ph->Rid);

	wprintf(L"%08Xh ", PTR_D(pb, &ph->MaskValid));
	wprintf(L"%01Xh ", sizeof(ph->MaskValid));
	wprintf(L"%11s ", L"MaskValid");
	wprintf(L"%016Xh ", ph->MaskValid);
	wprintf(L"(%d tables are valid)\n", num_of_1_u8(ph->MaskValid));

	wprintf(L"%08Xh ", PTR_D(pb, &ph->MaskSorted));
	wprintf(L"%01Xh ", sizeof(ph->MaskSorted));
	wprintf(L"%11s ", L"MaskSorted");
	wprintf(L"%016Xh\n", ph->MaskSorted);

	wprintf(L"\n");

	{
		u4 *p_rows = (u4 *)((byte *)&ph->MaskSorted + sizeof(ph->MaskSorted));
		u2 i;

		for(i = 0; i < NUMBER_OF_METADATA_TABLES; ++i)
		{
			if(ph->MaskValid & ((u8)1 << i))
			{
				wprintf(L"%08Xh ", PTR_D(pb, p_rows));
				wprintf(L"%01Xh ", sizeof(u4));
				wprintf(L"Rows of %-23s ", get_metadata_table_name(i));
				wprintf(L"%08Xh (%d)\n", *p_rows, *p_rows);

				++p_rows;
			}
		}
	}
}

void print_mdt_list(pe_file *p_pe, BOOL with_col)
{
	u2 tid;
	mdt_tbl_def *p_tbl;

	if(!check_file_opened() || !check_pe_headers(p_pe) || !check_metadata(p_pe))
		return;

	wprintf(L"index    name                   col_num record_size is_valid record_num             rid_size is_sorted data_offset\n");
	wprintf(L"-------- ---------------------- ------- ----------- -------- ---------------------- -------- --------- -----------\n");
	//        00h (00) GenericParamConstraint 9       20h (32)    Y        FFFFFFFFh (4294967295) 2        Y         1234ABCDh

	for(tid = 0, p_tbl = p_pe->metadata.tbl_defs;
		tid < NUMBER_OF_METADATA_TABLES;
		++tid, ++p_tbl)
	{
		if(tid > 0 && with_col)
		{
			wprintf(L"\n");
			wprintf(L"index    name                   col_num record_size is_valid record_num             rid_size is_sorted data_offset\n");
			wprintf(L"-------- ---------------------- ------- ----------- -------- ---------------------- -------- --------- -----------\n");
		}

		wprintf(L"%02Xh (%2d) %-22s %-7d %2Xh (%2d)    %-8s %8Xh (%10d) %-8d %-9s %08Xh\n",
			tid, tid,
			p_tbl->name,
			p_tbl->col_num,
			p_tbl->record_size, p_tbl->record_size,
			p_tbl->is_valid ? L"Y" : L"N",
			p_tbl->record_num, p_tbl->record_num,
			p_tbl->rid_size,
			p_tbl->is_sorted ? L"Y" : L"N",
			PTR_D(p_pe->p_base, p_tbl->p_data));

		if(with_col)
		{
			int col_idx;
			mdt_col_def *p_col;
			wchar_t str_buf[S_MAX_COL_TYPE_NAME_LEN];

			wprintf(L"\n");
			wprintf(L"    col_name          col_size col_offset col_type\n");
			wprintf(L"    ----------------- -------- ---------- ------------------------------\n");

			for(col_idx = 0, p_col = p_pe->metadata.col_defs + p_tbl->cols;
				col_idx < p_tbl->col_num;
				++col_idx, ++p_col)
			{
				s_format_col_type_name(p_pe, p_col->type, str_buf, S_MAX_COL_TYPE_NAME_LEN - 1);

				wprintf(L"    %-17s %-8d %2Xh (%2d)   %s\n",
					p_col->name,
					p_col->size,
					p_col->offset, p_col->offset,
					str_buf);
			}
		}
	}
}

void print_mdt_ct(pe_file *p_pe, BOOL with_rt)
{
	u1 ctid, tag_id;
	table_id tbl_id;
	mdt_tbl_def *p_tbls = p_pe->metadata.tbl_defs;
	coded_token_def *p_ct = p_pe->metadata.coded_tokens;
	coded_token_ref_tbl_def *p_ctrt = p_pe->metadata.coded_token_ref_tbls;


	if(!check_file_opened() || !check_pe_headers(p_pe) || !check_metadata(p_pe))
		return;

	wprintf(L"name                tag_bits tables record_limit  column_size\n");
	wprintf(L"------------------- -------- ------ ------------- -----------\n");
	//        CustomAttributeType 5        22     7FFFh (32767) 4

	for(ctid = 0; ctid < MDCOLT_CT__COUNT; ++ctid)
	{
		if(ctid > 0 && with_rt)
		{
			wprintf(L"\n");
			wprintf(L"name                tag_bits tables record_limit  column_size\n");
			wprintf(L"------------------- -------- ------ ------------- -----------\n");
		}

		wprintf(L"%-19s %-8d %-6d %04Xh (%5d) %d\n",
			p_ct[ctid].name,
			p_ct[ctid].tag_size,
			p_ct[ctid].tbl_num,
			p_ct[ctid].record_limit, p_ct[ctid].record_limit,
			p_ct[ctid].col_size);

		if(with_rt)
		{
			wprintf(L"\n");
			wprintf(L"    tag referenced_table       record_number\n");
			wprintf(L"    --- ---------------------- ----------------------\n");
			//            1   GenericParamConstraint FFFFFFFFh (4294967295)
			for(tag_id = 0; tag_id < p_ct[ctid].tbl_num; ++tag_id)
			{
				tbl_id = p_ctrt[p_ct[ctid].tables + tag_id].table_id;
				wprintf(L"    %-3d %-22s %8Xh (%10d)\n",
					tag_id,
					tbl_id < NUMBER_OF_METADATA_TABLES ? p_tbls[tbl_id].name : L"<UNUSED>",
					p_tbls[tbl_id].record_num, p_tbls[tbl_id].record_num);
			}
		}
	}
}

void print_mdt_tbl(pe_file *p_pe, table_id tid,
	size_t start, size_t length, BOOL single_line)
{
	mdt_tbl_def *p_tbl;
	mdt_col_def *p_col;
	size_t rid, colid;
	byte *p_row;
	u4 col_val;
	wchar_t col_val_str[S_MAX_COL_VAL_LEN];

	if(!check_file_opened() || !check_pe_headers(p_pe) || !check_metadata(p_pe))
		return;

	p_tbl = p_pe->metadata.tbl_defs + tid;

	wprintf(L"%s (%02Xh, %d), %d records in total.\n",
		p_tbl->name, tid, tid, p_tbl->record_num);

	if(start < 1 || start > p_tbl->record_num
		|| start + length - 1 < 1 || start + length - 1 > p_tbl->record_num)
	{
		wprintf(L"Row index out of range.\n");
		return;
	}

	for(rid = start, p_row = p_tbl->p_data + (start - 1) * p_tbl->record_size;  // start based on 1
		rid < start + length;
		++rid, p_row += p_tbl->record_size)
	{
		if(rid > start)
			wprintf(L"\n");

		wprintf(L"Record[%Xh (%d)], offset=%08Xh, size=%Xh (%d)\n",
			rid, rid, PTR_D(p_pe->p_base, p_row), p_tbl->record_size, p_tbl->record_size);

		for(colid = 0, p_col = p_pe->metadata.col_defs + p_tbl->cols;
			colid < p_tbl->col_num;
			++colid, ++p_col)
		{
			wprintf(L"    %08Xh %2Xh %-17s ",
				PTR_D(p_pe->p_base, p_row + p_col->offset),
				p_col->size,
				p_col->name);

			if(p_col->size == 4)
				wprintf(L"%08Xh ", col_val = *(u4 *)(p_row + p_col->offset));
			else if(p_col->size == 2)
				wprintf(L"    %04Xh ", col_val = *(u2 *)(p_row + p_col->offset));
			else if(p_col->size == 1)
				wprintf(L"      %02Xh ", col_val = *(u1 *)(p_row + p_col->offset));
			else
				col_val = 0;

			s_format_col_val(p_pe, p_tbl, p_col, col_val, col_val_str, S_MAX_COL_VAL_LEN - 1);
			wprintf(L"%s\n", col_val_str);
		}
	}
}
